/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufpr.inf.escel.brain.operators;

import br.ufpr.inf.escel.brain.model.DadoTeste;
import br.ufpr.inf.escel.brain.model.Individual;
import java.util.ArrayList;
import java.util.Collection;

/**
 *
 * @author gustavo
 * @since 1.6
 */
public abstract class Operadores {

    /**
     * Realiza um cruzamento entre dois individuos da população, 
     * gerando um novo descendente, com carga genética similar
     * aos dos antepassados.
     *
     * @param ind1 pai
     * @param ind2 mãe
     * @since 1.6
     */
    public abstract DadoTeste cruzar(DadoTeste dt1, DadoTeste dt2);

    /**
     * Realiza uma pequena alteranção na estrutura do cromossomo
     *
     * @param ind que sofrerá mutação
     * @since 1.6
     */
    public abstract DadoTeste mutar(DadoTeste dt);

    /**
     * Realiza o relacionamento entre o conjunto de individuos,
     * de maneira a preservar a diversidade dos conjuntos.
     * 
     * @param ind1, ind2, ind3, ..., indn-1.
     * TODO relacionamento de individuos aleatórios (usar probabilidade de 50%)
     */
    public Individual[] relacionar(Individual ind1, Individual ind2) {

        Individual aux1 = ind1.copy();
        Individual aux2 = ind2.copy();

        int half = ind1.getDadosTeste().size() / 2;

        for (int i = 0; i < ind1.getDadosTeste().size(); i++) {

            //recebe metade de cada individuo
            if (i >= half) {
                aux1.update(ind1.getDadoTeste(i), ind2.getDadoTeste(i));
                aux2.update(ind2.getDadoTeste(i), ind1.getDadoTeste(i));
            } else {
                aux1.update(ind2.getDadoTeste(i), ind1.getDadoTeste(i));
                aux2.update(ind1.getDadoTeste(i), ind1.getDadoTeste(i));
            }
        }

        Individual[] out = {aux1, aux2};

        return out;
    }

    /**
     * Implementação da clássica roleta dos algoritmos genéticos
     * @param individuo
     * @return dado de teste selecionado
     */
    public DadoTeste selecionar(Individual ind) {

//        if (Constantes.TIPO_PROGRAMA.equals("PP")
//                && Constantes.FITNESS_DADO_TESTE.equals("POKETOOL")) {
//            ind = atribuiBonusPorSimilaridade(ind);
//        }

        double somatoriodosFitness = 0;
        double fitnessAcumulado = 0;

        for (DadoTeste dadoTeste : ind.getDadosTeste()) {
            somatoriodosFitness += dadoTeste.getFitness();
        }

        Collection<Object[]> roleta = new ArrayList<Object[]>();

        for (DadoTeste dadoTeste : ind.getDadosTeste()) {
            roleta.add(new Object[]{
                        new Double(fitnessAcumulado),
                        new Double(fitnessAcumulado + dadoTeste.getFitness() / somatoriodosFitness),
                        dadoTeste
                    });

            fitnessAcumulado += dadoTeste.getFitness() / somatoriodosFitness;
        }

        DadoTeste retorno = null;

        double sorteio = Math.random();
        for (Object item[] : roleta) {
            double limiteInferior = ((Double) item[0]).doubleValue();
            double limiteSuperior = ((Double) item[1]).doubleValue();
            DadoTeste dadoDaVez = (DadoTeste) item[2];

            if (sorteio >= limiteInferior && sorteio <= limiteSuperior) {
                retorno = dadoDaVez.copy();
                break;
            }
        }

        return retorno;
    }
}
